//
// Created by taojiaheng on 2020/5/27.
//
#ifndef WEBSOCKETSERVER_SOCKET_H
#define WEBSOCKETSERVER_SOCKET_H
#include "../common/cdef.h"
#include "Protocol/Protocol.h"
#include "Event.h"
#include "Tool/Buff.h"
#include "../common/function.h"


/**
 * socket 链接对象
 */
class Socket;
class TimerTask;
class Connection{
public:
    // 套接字
    int fd;
    // 地址结构
    sockaddr_in client_addr;
    //id
    unsigned long long client_id;
    Connection(int _fd, sockaddr_in _client_addr);
    Connection();
    ~Connection();
    // 对象字符串表示
    char * toString();
    // 应用层使用的指针
    void * appPtr;
    // 系统套接字
    Socket * server_sock;
    // 每个链接缓冲区
    VariableBuff * conn_buffer;
    // 链接是否握手
    bool is_handshake = false;
    // 链接协议数据包大小 0 = 不知道
    int current_package_size = 0;
    // 发送数据 经过协议编码
    int sendToCurrentClient(BUFF * buff);
    // 上一次会话时间
    long last_session_time;
    // 链接是否链接
    bool is_connect;
    // 链接是否在处理信息
    bool is_message;
};

class Socket {
private:
    // 监听套接字
    int server_fd;
    // 事件分发函数 服务器
    void handleEvents(epoll_event * events, int num);
    // 处理接受事件
    void doAccept();
    // 处理读取事件
    void doRead(long long * client_id_ptr);
    // 添加一个套接字到epoll监听队列
    int addEvent(int fd, int state, void *);
    // 添加一个套接字到epoll监听队列
    void deleteEvent(int fd, int state);
    // 修改一个套接字到epoll到监听队列
    void modifyEvent(int fd, int state);
    void modifyEvent(int fd, int state, void * data);
    // 处理消息函数指针
    void(*message_handler)(Connection * client, BUFF * buff);
    // 处理套接字链接处理函数指针
    void(*connect_handler)(Connection * client);
    // 处理套接字断开处理函数指针
    void(*close_handler)(Connection * client);
    // 处理协议握手成功处理函数
    void(*protocol_connect_handler)(Connection * client);
    // 服务器地址
    struct sockaddr_in server_addr;
    // 触发套接字链接回调
    void triggerConnectHandler(Connection *);
    // 触发处理消息回调
    void triggerMessageHandler(Connection * client, BUFF * buff);
    // 触发套接字关闭回调
    void triggerCloseHandler(Connection *);
    // 客户端事件分发函数
    void connectEventHandler(epoll_event * events, int num);
    // 触发协议握手成功回调
    void triggerProtocolConnectHandler(Connection *);
    // 事件类指针
    Event * eventHandler;
    // 链接的集合
    std::unordered_map<long long, Connection *> * connection_list;
    // 从链接的集合中移除一个链接对象 但 不会释放对象
    bool removeConnectionFormList(long long client_id);
    // 执行心跳检测逻辑
    void connectionPingInterval();
    // 心跳检测定时任务对象
    TimerTask * ping_interval_task;
public:
    Socket();
    ~Socket();
    // 初始化 服务器套接字
    void initServer(const char *, int);
    // 运行服务器
    void runServer();
    // 初始化客户端套接字
    void initConnectPoll();
    // 链接服务器
    long long connectServer(const char * ip, int port);
    /**
     * 运行客户链接 这会在一个新的进程中运行
     * @param is_new_thread 是否在一个新的线程中运行服务器
     */
    void runConnect(bool is_new_thread);
    // 设置当数据响应时的回调
    void setMessageHandler(void(*_message_handler)(Connection *, BUFF * buff));
    // 设置套接字链接时的回调
    void setConnectHandler(void(*_connect_handler)(Connection *));
    // 设置套接字断开时的回调
    void setCloseHandler(void(*_close_handler)(Connection *));
    // 设置协议握手成功的回调
    void setProtocolConnectHandler(void(*_protocol_connect_handler)(Connection * client));
    // 根据 sockaddr_in 计算链接唯一ID
    static unsigned long long getUnionId(sockaddr_in * addr);
    // 关闭一个链接
    void closeClient(Connection *);
    void closeClient(std::unordered_map<long long, Connection*>::iterator it);
    // 设置协议
    void setProtocol(Protocol *);
    // epoll 套接字
    int epoll_fd;
    // 获取服务器地址
    sockaddr_in * getServerAddr();
    // 设置事件响应类指针
    void setEventHandler(Event * e);
    // 协议类地址
    Protocol * protocol;
    // 是否是服务器
    bool is_server;
    // 返回链接集合实例
    std::unordered_map<long long, Connection*> * getConnectionList();
    // 心跳检测的时间间隔 0代表没有间隔
    int connection_ping_interval = 0;
    // 从链接对象集合中获得一个链接对象 如果没有返回NULL
    Connection * getConnectionFormList(long long * client_id);
    // 检查一个client_id是否存在
    bool checkClientIdIsExist(long long client_id);
    // 发送数据到一个client_id 经过协议编码
    int sendToClient(long long client_id, BUFF * buff);
};


#endif //WEBSOCKETSERVER_SOCKET_H
